// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraPawnExtensionComponent.h"

#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Components/GameFrameworkComponentDelegates.h"
#include "Components/GameFrameworkComponentManager.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Pawn.h"
#include "LyraGameplayTags.h"
#include "LyraLogChannels.h"
#include "LyraPawnData.h"
#include "Net/UnrealNetwork.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraPawnExtensionComponent)

class FLifetimeProperty;
class UActorComponent;

const FName ULyraPawnExtensionComponent::NAME_ActorFeatureName("PawnExtension"); // Actor功能名称

/**
 * 构造函数
 */
ULyraPawnExtensionComponent::ULyraPawnExtensionComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) // 调用父类构造函数
{
	PrimaryComponentTick.bStartWithTickEnabled = false; // 主组件Tick开始时禁用
	PrimaryComponentTick.bCanEverTick = false; // 主组件永远不能Tick

	SetIsReplicatedByDefault(true); // 默认设置为复制

	PawnData = nullptr; // Pawn数据
	AbilitySystemComponent = nullptr; // 能力系统组件
}

/**
 * 获取生命周期复制属性
 */
void ULyraPawnExtensionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 调用父类方法

	DOREPLIFETIME(ULyraPawnExtensionComponent, PawnData); // 复制Pawn数据
}

/**
 * 注册时调用
 */
void ULyraPawnExtensionComponent::OnRegister()
{
	Super::OnRegister(); // 调用父类方法

	const APawn* Pawn = GetPawn<APawn>(); // 获取Pawn
	ensureAlwaysMsgf((Pawn != nullptr), TEXT("LyraPawnExtensionComponent on [%s] can only be added to Pawn actors."), *GetNameSafe(GetOwner())); // 确保始终是Pawn Actor

	TArray<UActorComponent*> PawnExtensionComponents; // Pawn扩展组件数组
	Pawn->GetComponents(ULyraPawnExtensionComponent::StaticClass(), PawnExtensionComponents); // 获取Pawn扩展组件
	ensureAlwaysMsgf((PawnExtensionComponents.Num() == 1), TEXT("Only one LyraPawnExtensionComponent should exist on [%s]."), *GetNameSafe(GetOwner())); // 确保始终只有一个Pawn扩展组件

	// 尽早向初始化状态系统注册，这仅在这是游戏世界时有效
	RegisterInitStateFeature(); // 注册初始化状态功能
}

/**
 * 开始游戏时调用
 */
void ULyraPawnExtensionComponent::BeginPlay()
{
	Super::BeginPlay(); // 调用父类方法

	// 监听所有功能的变化
	BindOnActorInitStateChanged(NAME_None, FGameplayTag(), false); // 绑定Actor初始化状态改变
	
	// 通知状态管理器我们已生成，然后尝试其余的默认初始化
	ensure(TryToChangeInitState(LyraGameplayTags::InitState_Spawned)); // 确保尝试改变初始化状态为已生成
	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * 结束游戏时调用
 */
void ULyraPawnExtensionComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	UninitializeAbilitySystem(); // 取消初始化能力系统
	UnregisterInitStateFeature(); // 取消注册初始化状态功能

	Super::EndPlay(EndPlayReason); // 调用父类方法
}

/**
 * 设置Pawn数据
 */
void ULyraPawnExtensionComponent::SetPawnData(const ULyraPawnData* InPawnData)
{
	check(InPawnData); // 检查Pawn数据

	APawn* Pawn = GetPawnChecked<APawn>(); // 获取Pawn

	if (Pawn->GetLocalRole() != ROLE_Authority) // 如果本地角色不是权威
	{
		return; // 返回
	}

	if (PawnData) // 如果Pawn数据已存在
	{
		UE_LOG(LogLyra, Error, TEXT("Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]."), *GetNameSafe(InPawnData), *GetNameSafe(Pawn), *GetNameSafe(PawnData)); // 记录错误日志
		return; // 返回
	}

	PawnData = InPawnData; // 设置Pawn数据

	Pawn->ForceNetUpdate(); // 强制网络更新

	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * Pawn数据复制时调用
 */
void ULyraPawnExtensionComponent::OnRep_PawnData()
{
	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * 初始化能力系统
 */
void ULyraPawnExtensionComponent::InitializeAbilitySystem(ULyraAbilitySystemComponent* InASC, AActor* InOwnerActor)
{
	check(InASC); // 检查能力系统组件
	check(InOwnerActor); // 检查所有者Actor

	if (AbilitySystemComponent == InASC) // 如果能力系统组件未改变
	{
		// 能力系统组件没有改变。
		return; // 返回
	}

	if (AbilitySystemComponent) // 如果能力系统组件已存在
	{
		// 清理旧的能力系统组件。
		UninitializeAbilitySystem(); // 取消初始化能力系统
	}

	APawn* Pawn = GetPawnChecked<APawn>(); // 获取Pawn
	AActor* ExistingAvatar = InASC->GetAvatarActor(); // 获取现有化身

	UE_LOG(LogLyra, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "), *GetNameSafe(InASC), *GetNameSafe(Pawn), *GetNameSafe(InOwnerActor), *GetNameSafe(ExistingAvatar)); // 记录详细日志

	if ((ExistingAvatar != nullptr) && (ExistingAvatar != Pawn)) // 如果现有化身存在且不等于Pawn
	{
		UE_LOG(LogLyra, Log, TEXT("Existing avatar (authority=%d)"), ExistingAvatar->HasAuthority() ? 1 : 0); // 记录日志

		// 已经有一个Pawn作为ASC的化身，所以我们需要将其踢出
		// 这在客户端上可能发生，如果它们延迟：在移除死亡Pawn之前，新的Pawn已生成+拥有
		ensure(!ExistingAvatar->HasAuthority()); // 确保现有化身没有权威

		if (ULyraPawnExtensionComponent* OtherExtensionComponent = FindPawnExtensionComponent(ExistingAvatar)) // 如果找到其他Pawn扩展组件
		{
			OtherExtensionComponent->UninitializeAbilitySystem(); // 取消初始化能力系统
		}
	}

	AbilitySystemComponent = InASC; // 设置能力系统组件
	AbilitySystemComponent->InitAbilityActorInfo(InOwnerActor, Pawn); // 初始化能力Actor信息

	if (ensure(PawnData)) // 确保Pawn数据存在
	{
		InASC->SetTagRelationshipMapping(PawnData->TagRelationshipMapping); // 设置标签关系映射
	}

	OnAbilitySystemInitialized.Broadcast(); // 广播能力系统初始化
}

/**
 * 取消初始化能力系统
 */
void ULyraPawnExtensionComponent::UninitializeAbilitySystem()
{
	if (!AbilitySystemComponent) // 如果能力系统组件不存在
	{
		return; // 返回
	}

	// 如果我们仍然是化身Actor，则取消初始化ASC（否则另一个Pawn在成为化身Actor时已经做了）
	if (AbilitySystemComponent->GetAvatarActor() == GetOwner()) // 如果化身Actor是所有者
	{
		FGameplayTagContainer AbilityTypesToIgnore; // 要忽略的能力类型
		AbilityTypesToIgnore.AddTag(LyraGameplayTags::Ability_Behavior_SurvivesDeath); // 添加存活死亡行为标签

		AbilitySystemComponent->CancelAbilities(nullptr, &AbilityTypesToIgnore); // 取消能力
		AbilitySystemComponent->ClearAbilityInput(); // 清除能力输入
		AbilitySystemComponent->RemoveAllGameplayCues(); // 移除所有游戏提示

		if (AbilitySystemComponent->GetOwnerActor() != nullptr) // 如果所有者Actor不为空
		{
			AbilitySystemComponent->SetAvatarActor(nullptr); // 设置化身Actor为空
		}
		else // 否则
		{
			// 如果ASC没有有效的所有者，我们需要清除*所有*Actor信息，而不仅仅是化身配对
			AbilitySystemComponent->ClearActorInfo(); // 清除Actor信息
		}

		OnAbilitySystemUninitialized.Broadcast(); // 广播能力系统取消初始化
	}

	AbilitySystemComponent = nullptr; // 清空能力系统组件
}

/**
 * 处理控制器改变
 */
void ULyraPawnExtensionComponent::HandleControllerChanged()
{
	if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked<APawn>())) // 如果能力系统组件存在且化身Actor是Pawn
	{
		ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor()); // 确保能力Actor信息所有者Actor等于能力系统组件所有者Actor
		if (AbilitySystemComponent->GetOwnerActor() == nullptr) // 如果所有者Actor为空
		{
			UninitializeAbilitySystem(); // 取消初始化能力系统
		}
		else // 否则
		{
			AbilitySystemComponent->RefreshAbilityActorInfo(); // 刷新能力Actor信息
		}
	}

	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * 处理玩家状态复制
 */
void ULyraPawnExtensionComponent::HandlePlayerStateReplicated()
{
	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * 设置玩家输入组件
 */
void ULyraPawnExtensionComponent::SetupPlayerInputComponent()
{
	CheckDefaultInitialization(); // 检查默认初始化
}

/**
 * 检查默认初始化
 */
void ULyraPawnExtensionComponent::CheckDefaultInitialization()
{
	// 在检查我们的进度之前，尝试进展我们可能依赖的任何其他功能
	CheckDefaultInitializationForImplementers(); // 检查实现者的默认初始化

	static const TArray<FGameplayTag> StateChain = { LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable, LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady }; // 状态链

	// 这将尝试从已生成（仅在BeginPlay中设置）通过数据初始化阶段进展，直到达到游戏准备就绪
	ContinueInitStateChain(StateChain); // 继续初始化状态链
}

/**
 * 检查是否可以改变初始化状态
 */
bool ULyraPawnExtensionComponent::CanChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) const
{
	check(Manager); // 检查管理器

	APawn* Pawn = GetPawn<APawn>(); // 获取Pawn
	if (!CurrentState.IsValid() && DesiredState == LyraGameplayTags::InitState_Spawned) // 如果当前状态无效且期望状态是已生成
	{
		// 只要我们在有效的Pawn上，就算作已生成
		if (Pawn) // 如果Pawn存在
		{
			return true; // 返回true
		}
	}
	if (CurrentState == LyraGameplayTags::InitState_Spawned && DesiredState == LyraGameplayTags::InitState_DataAvailable) // 如果当前状态是已生成且期望状态是数据可用
	{
		// 需要Pawn数据。
		if (!PawnData) // 如果没有Pawn数据
		{
			return false; // 返回false
		}

		const bool bHasAuthority = Pawn->HasAuthority(); // 是否有权威
		const bool bIsLocallyControlled = Pawn->IsLocallyControlled(); // 是否本地控制

		if (bHasAuthority || bIsLocallyControlled) // 如果有权威或本地控制
		{
			// 检查是否被控制器拥有。
			if (!GetController<AController>()) // 如果没有控制器
			{
				return false; // 返回false
			}
		}

		return true; // 返回true
	}
	else if (CurrentState == LyraGameplayTags::InitState_DataAvailable && DesiredState == LyraGameplayTags::InitState_DataInitialized) // 如果当前状态是数据可用且期望状态是数据已初始化
	{
		// 如果所有功能都已达到数据可用状态，则转换到初始化
		return Manager->HaveAllFeaturesReachedInitState(Pawn, LyraGameplayTags::InitState_DataAvailable); // 返回是否所有功能都已达到初始化状态
	}
	else if (CurrentState == LyraGameplayTags::InitState_DataInitialized && DesiredState == LyraGameplayTags::InitState_GameplayReady) // 如果当前状态是数据已初始化且期望状态是游戏准备就绪
	{
		return true; // 返回true
	}

	return false; // 返回false
}

/**
 * 处理改变初始化状态
 */
void ULyraPawnExtensionComponent::HandleChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState)
{
	if (DesiredState == LyraGameplayTags::InitState_DataInitialized) // 如果期望状态是数据已初始化
	{
		// 这目前由监听此状态变化的其他组件处理
	}
}

/**
 * Actor初始化状态改变时调用
 */
void ULyraPawnExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
{
	// 如果另一个功能现在处于DataAvailable，查看我们是否应该转换到DataInitialized
	if (Params.FeatureName != NAME_ActorFeatureName) // 如果功能名称不是Actor功能名称
	{
		if (Params.FeatureState == LyraGameplayTags::InitState_DataAvailable) // 如果功能状态是数据可用
		{
			CheckDefaultInitialization(); // 检查默认初始化
		}
	}
}

/**
 * 注册到能力系统初始化委托并调用
 */
void ULyraPawnExtensionComponent::OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate Delegate)
{
	if (!OnAbilitySystemInitialized.IsBoundToObject(Delegate.GetUObject())) // 如果未绑定到委托对象
	{
		OnAbilitySystemInitialized.Add(Delegate); // 添加委托
	}

	if (AbilitySystemComponent) // 如果能力系统组件存在
	{
		Delegate.Execute(); // 执行委托
	}
}

/**
 * 注册到能力系统取消初始化委托
 */
void ULyraPawnExtensionComponent::OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate Delegate)
{
	if (!OnAbilitySystemUninitialized.IsBoundToObject(Delegate.GetUObject())) // 如果未绑定到委托对象
	{
		OnAbilitySystemUninitialized.Add(Delegate); // 添加委托
	}
}